#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>

#include "kvsp.h"

static void setcompack(COMPACK *pcmpk, int command, int key_len, int value_len);
static int writen(int sockfd, char *buffer, int size);
static int readn(int sockfd, char *buffer, int size);

int conn_serv(struct sockaddr_in *servaddr)
{
    int sockfd;
    if (servaddr==NULL)
    {
        fprintf(stderr, "coonserv failed:servaddr can not be NULL\n");
        exit(-1);
    }
    if ((sockfd=socket(AF_INET, SOCK_STREAM, 0))<0)
    {
        perror("open socket file failed");
        exit(-1);
    }

    if (connect(sockfd, (struct sockaddr*)servaddr, (socklen_t)sizeof(struct sockaddr))<0)
    {
        perror("connect to server failed");
        exit(-1);
    }

    return sockfd;
} 

int data_trans(int command, int sockfd, char *key, int key_len, char **value, int *value_len)
{
    COMPACK cmpk;
    if (command == PUT&&*value_len>=5*1024*1024)
    {
        fprintf(stderr, "value size shouldn't be large than 5M\n");
        return -1;
    }
    setcompack(&cmpk, command, key_len, command==PUT?*value_len:0);
    
    if(writen(sockfd, (char *)&cmpk, sizeof(cmpk))!=sizeof(cmpk))
    {
        fprintf(stderr,"send command failed!\n");
        return -1;
    }
    
    if (writen(sockfd,key,key_len)!=key_len)
    {
        fprintf(stderr, "send data failed!\n");
        return -1;
    }
    if (PUT == command)
    {
        if (writen(sockfd, *value, *value_len)!=*value_len)
        {
            fprintf(stderr, "send data failed!\n");
            return -1;
        }
    }
    
    if (readn(sockfd, (char *)&cmpk, sizeof(cmpk))!=sizeof(cmpk))
    {
        fprintf(stderr, "read server status failed!\n");
        return -1;
    }

    if (cmpk.type==0)
    {
        if (cmpk.value_length>0)
        {
            *value = (char *)malloc(cmpk.value_length);
            *value_len=cmpk.value_length;
            if (readn(sockfd, *value, *value_len)==-1)
            {
                fprintf(stderr,"read data failed!\n");
                return -1;
            }
        } 
    }
    else 
        return -1;
    
    return 0;
}

static void setcompack(COMPACK *pcmpk, int command, int key_len, int value_len)
{
    pcmpk->type=command;
    pcmpk->key_length=key_len;
    pcmpk->value_length=value_len;
    pcmpk->filling=0;
}

static int writen(int fd, char *buffer, int size)
{
    int n, towrite;
    for (towrite=size; towrite>0; buffer+=n,towrite-=n)
    {
        n=write(fd,buffer,towrite);
        if (n <=0 )
            break;
    }
    if (towrite)
        return -1;
    return size;
}

static int readn(int fd, char *buffer, int size)
{
    int n, toread;
    for (toread=size; toread>0; buffer+=n,toread-=n)
    {
        n=read(fd,buffer,toread);
        if (n == -1 && errno == EFAULT)
            return -1;
        if (n <= 0)
            break;
    }
    if (toread)
        return -1;
    return size;
}
